package org.javahispano.javacup.tacticas_aceptadas.nchar;

import java.awt.Color;
import java.util.Iterator;
import java.util.List;
import java.util.LinkedList;
import java.util.Random;

import javax.swing.text.Position;

import org.javahispano.javacup.modelo.*;

public class TacticaVillaPancha implements Tactica {

    Posicion alineacion1[] = new Posicion[]{
        //new Posicion(0.2595419847328244, -50.41044776119403),
        new Posicion(0.2595419847328244, -Constantes.LARGO_CAMPO_JUEGO / 2),
        new Posicion(9.034965034965035, -33.49547511312217),
        new Posicion(-8.797202797202797, -29.694570135746606),
        new Posicion(27.104895104895103, -26.84389140271493),
        new Posicion(0.23776223776223776, -7.364253393665159),
        new Posicion(-25.44055944055944, -23.28054298642534),
        new Posicion(16.405594405594407, 33.49547511312217),
        new Posicion(22.58741258741259, 4.513574660633484),
        new Posicion(0.0, 41.334841628959275),
        new Posicion(-24.48951048951049, 14.25339366515837),
        new Posicion(0.951048951048951, 19.2420814479638)
    };
    Posicion alineacion2[] = new Posicion[]{
        new Posicion(0.2595419847328244, -50.41044776119403),
        new Posicion(-11.16030534351145, -31.082089552238806),
        new Posicion(11.16030534351145, -31.6044776119403),
        new Posicion(27.251908396946565, -27.94776119402985),
        new Posicion(-29.84732824427481, -26.902985074626866),
        new Posicion(8.564885496183205, -7.574626865671642),
        new Posicion(-10.641221374045802, -7.052238805970149),
        new Posicion(27.251908396946565, 4.440298507462686),
        new Posicion(-29.32824427480916, 3.3955223880597014),
        new Posicion(-0.2595419847328244, 19.067164179104477),
        new Posicion(-0.2595419847328244, 35.78358208955224)
    };
    Posicion alineacion3[] = new Posicion[]{
        new Posicion(0.2595419847328244, -50.41044776119403),
        new Posicion(-11.16030534351145, -31.082089552238806),
        new Posicion(11.16030534351145, -31.6044776119403),
        new Posicion(26.732824427480914, -20.111940298507463),
        new Posicion(-29.32824427480916, -21.67910447761194),
        new Posicion(0.2595419847328244, -0.26119402985074625),
        new Posicion(-18.946564885496183, -0.26119402985074625),
        new Posicion(18.946564885496183, -0.26119402985074625),
        new Posicion(-19.46564885496183, 35.78358208955224),
        new Posicion(-0.2595419847328244, 19.067164179104477),
        new Posicion(18.946564885496183, 35.26119402985075)
    };
    Posicion alineacion4[] = new Posicion[]{
        new Posicion(0.2595419847328244, -50.41044776119403),
        new Posicion(-11.16030534351145, -31.082089552238806),
        new Posicion(11.16030534351145, -31.6044776119403),
        new Posicion(28.290076335877863, -28.470149253731343),
        new Posicion(-28.290076335877863, -28.470149253731343),
        new Posicion(11.16030534351145, -1.3059701492537314),
        new Posicion(-10.641221374045802, -0.7835820895522387),
        new Posicion(-27.251908396946565, 31.6044776119403),
        new Posicion(-10.641221374045802, 30.559701492537314),
        new Posicion(9.603053435114505, 28.992537313432837),
        new Posicion(25.69465648854962, 28.992537313432837)
    };
    Posicion alineacion5[] = new Posicion[]{
        new Posicion(0.2595419847328244, -50.41044776119403),
        new Posicion(11.174825174825173, -34.920814479638004),
        new Posicion(-6.181818181818182, -30.407239819004527),
        new Posicion(28.293706293706293, -31.59502262443439),
        new Posicion(1.902097902097902, -18.054298642533936),
        new Posicion(-28.531468531468533, -26.84389140271493),
        new Posicion(22.11188811188811, -2.8506787330316743),
        new Posicion(15.216783216783217, -9.97737556561086),
        new Posicion(-1.4265734265734267, -0.9502262443438915),
        new Posicion(-22.58741258741259, -2.3755656108597285),
        new Posicion(1.902097902097902, -1.1877828054298643)
    };
    Posicion alineacion6[] = new Posicion[]{
        new Posicion(0.2595419847328244, -50.41044776119403),
        new Posicion(9.986013986013985, -38.4841628959276),
        new Posicion(-8.321678321678322, -35.8710407239819),
        new Posicion(28.290076335877863, -28.470149253731343),
        new Posicion(-2.8531468531468533, -19.004524886877828),
        new Posicion(-27.34265734265734, -28.98190045248869),
        new Posicion(24.965034965034967, -5.9389140271493215),
        new Posicion(4.5174825174825175, -14.96606334841629),
        new Posicion(11.174825174825173, -2.3755656108597285),
        new Posicion(-25.678321678321677, -6.651583710407239),
        new Posicion(-10.937062937062937, -4.038461538461538)
    };

    class TacticaDetalleImpl implements TacticaDetalle {

        public String getNombre() {
            return "Villa Pancha";
        }

        public String getPais() {
            return "Uruguay";
        }

        public String getEntrenador() {
            return "Pachín";
        }

        public Color getColorCamiseta() {
            return new Color(153, 153, 255);
        }

        public Color getColorPantalon() {
            return new Color(255, 255, 255);
        }

        public Color getColorFranja() {
            return new Color(153, 153, 255);
        }

        public Color getColorCalcetas() {
            return new Color(153, 153, 255);
        }

        public Color getColorPortero() {
            return new Color(255, 204, 204);
        }

        public EstiloUniforme getEstilo() {
            return EstiloUniforme.SIN_ESTILO;
        }

        public Color getColorCamiseta2() {
            return new Color(141, 110, 150);
        }

        public Color getColorPantalon2() {
            return new Color(136, 77, 241);
        }

        public Color getColorFranja2() {
            return new Color(0, 178, 74);
        }

        public Color getColorCalcetas2() {
            return new Color(45, 251, 13);
        }

        public Color getColorPortero2() {
            return new Color(141, 90, 7);
        }

        public EstiloUniforme getEstilo2() {
            return EstiloUniforme.FRANJA_DIAGONAL;
        }

        class JugadorImpl implements JugadorDetalle {

            String nombre;
            int numero;
            Color piel, pelo;
            double velocidad, remate, presicion;
            boolean portero;
            Posicion posicion;

            public JugadorImpl(String nombre, int numero, Color piel, Color pelo,
                    double velocidad, double remate, double presicion, boolean portero) {
                this.nombre = nombre;
                this.numero = numero;
                this.piel = piel;
                this.pelo = pelo;
                this.velocidad = velocidad;
                this.remate = remate;
                this.presicion = presicion;
                this.portero = portero;
            }

            public String getNombre() {
                return nombre;
            }

            public Color getColorPiel() {
                return piel;
            }

            public Color getColorPelo() {
                return pelo;
            }

            public int getNumero() {
                return numero;
            }

            public boolean esPortero() {
                return portero;
            }

            public double getVelocidad() {
                return velocidad;
            }

            public double getRemate() {
                return remate;
            }

            public double getPresicion() {
                return presicion;
            }
        }

        public JugadorDetalle[] getJugadores() {
            return new JugadorDetalle[]{
                        new JugadorImpl("Anastasia Rava", 1, new Color(51, 51, 255), new Color(50, 0, 0), 1.0d, 1.0d, 1.0d, true),
                        new JugadorImpl("Jorge \"Manolo\" Bejar", 2, new Color(255, 200, 150), new Color(50, 0, 0), 1.0d, 0.84d, 1.0d, false),
                        new JugadorImpl("Diego \"No me decido\" Acosta", 3, new Color(255, 200, 150), new Color(50, 0, 0), 1.0d, 0.83d, 1.0d, false),
                        new JugadorImpl("Rodrigo \"Cyber de la pradera\" Méndez", 4, new Color(255, 200, 150), new Color(50, 0, 0), 0.77d, 0.36d, 0.37d, false),
                        new JugadorImpl("Nicolás \"Achechino\" Charbonnier", 5, new Color(255, 200, 150), new Color(50, 0, 0), 1.0d, 1.0d, 0.5d, false),
                        new JugadorImpl("Santiago \"Diablo on rails\" Pastorino", 6, new Color(255, 200, 150), new Color(50, 0, 0), 0.76d, 0.37d, 0.37d, false),
                        new JugadorImpl("Diego \"Costilla\" Marmissolle", 7, new Color(255, 200, 150), new Color(50, 0, 0), 0.75d, 1.0d, 1.0d, false),
                        new JugadorImpl("Marcelo \"Pela\" Perelmuter", 8, new Color(255, 200, 150), new Color(50, 0, 0), 0.5d, 0.5d, 1.0d, false),
                        new JugadorImpl("José \"Niño Goleador\" Costa", 9, new Color(255, 200, 150), new Color(50, 0, 0), 1.0d, 1.0d, 1.0d, false),
                        new JugadorImpl("Gustavo \"Bochae\" Quevado", 10, new Color(255, 200, 150), new Color(50, 0, 0), 0.8d, 0.78d, 1.0d, false),
                        new JugadorImpl("Santiago \"Calderita\" Montico", 11, new Color(255, 200, 150), new Color(50, 0, 0), 1.0d, 1.0d, 1.0d, false)
                    };
        }
    }
    TacticaDetalle detalle = new TacticaDetalleImpl();

    public TacticaDetalle getDetalle() {
        return detalle;
    }

    public Posicion[] getPosicionSaca(SituacionPartido sp) {
        return alineacion5;
    }

    public Posicion[] getPosicionRecive(SituacionPartido sp) {
        return alineacion6;
    }
    Random rand = new Random();
    LinkedList<Comando> comandos = new LinkedList<Comando>();

    public boolean paseSeguro(Posicion pos1, Posicion pos2, SituacionPartido sp) {
        for (Posicion rival : sp.rivales()) {
            // me dijo si esta cerca del destino algun rival
            if (rival.distancia(pos2) < 5) {
                return false;
            }

            // me fijo si en la zona del pase hay intrusos en el espectaculo
            double opuestoX = pos2.getX();
            double minX, maxX, minY, maxY;
            if (pos2.getX() > pos1.getX()) {
                opuestoX += 3;
                maxX = opuestoX;
                minX = pos1.getX();
            } else {
                opuestoX -= 3;
                minX = opuestoX;
                maxX = pos1.getX();
            }

            double opuestoY = pos2.getY();
            if (pos2.getY() > pos1.getY()) {
                opuestoY += 3;
                maxY = opuestoY;
                minY = pos1.getY();
            } else {
                opuestoY -= 3;
                minY = opuestoY;
                maxY = pos1.getY();
            }

            if (minX < rival.getX() && rival.getX() < maxX &&
                    minY < rival.getY() && rival.getY() < maxY) {
                return false;
            }
        }

        return true;
    }


    // ******************************************************************
    // ******************************************************************
    enum Grosso {

        Rava, Manolo, Diegoa, Cyber, Chino, Pasto, Costilla, Pela, Josexo, Bochae, Montico, Ninguno
    };

    enum Sector {

        Ataque, Medio, Defensa
    };

    enum CuadranteDefensa {

        ExtremoIzquierda, MedioIzquierda, MedioDerecha, ExtremoDerecha
    };

    static double LARGO_SECTOR = Constantes.LARGO_CAMPO_JUEGO / 3;

    // Flag de tenemos la pelota
    boolean tenemosLaPelota = false;

    // el último jugador rival que le pegó a la pelota
    int ultimoChutadorRival = 0;
    
    Posicion balonAnterior = new Posicion(0,0);

    // variables globales para saber que pase estamos intentando dar
    Grosso indiceOrigenPase = Grosso.Ninguno;
    Grosso indiceDestinoPase = Grosso.Ninguno;

    // define la posicion a donde deberian ir los jugadores
    // por defecto es la posicion de alguna alineacion
    // se usa en caso de jugadores que marcan a alguien o que pican o que buscan la pelota
    Posicion posicionMoverA[] = alineacion1.clone();
    // ******************************************************************
    // ******************************************************************
    boolean costiquiPicando = false;
    boolean deboSacar = false;
    int iterSaco = 0;
    int cantidadGolpearBalon = 0;
    
    Grosso tapon  = Grosso.Ninguno;
    Grosso abejita = Grosso.Ninguno;
    

    Grosso getGrosso(int i) {
        for (Grosso g : Grosso.values()) {
            if (g.ordinal() == i) {
                return g;
            }
        }
        return Grosso.Ninguno;
    }

    Sector getSector(SituacionPartido sp) {
        double yPelota = sp.balon().moverPosicion(0, Constantes.LARGO_CAMPO_JUEGO / 2).getY();
        if (yPelota <= LARGO_SECTOR) {
            return Sector.Defensa;
        }
        if (yPelota >= LARGO_SECTOR * 2) {
            return Sector.Ataque;
        } else {
            return Sector.Medio;
        }
    }

    void actualizarTenemosLaPelota(SituacionPartido sp) {
        if (sp.sacaRival()) {
            tenemosLaPelota = false;
        } else if (sp.saco()) {
            tenemosLaPelota = true;
        } else {
            int maximoNuestro = 0;
            int maximoEllos = 0;
            for (int valor : sp.iteracionesParaRematar()) {
                if (maximoNuestro < valor) {
                    maximoNuestro = valor;
                }
            }
            int indice = 0;
            for (int valor : sp.iteracionesParaRematarRival()) {
                if (maximoEllos < valor) {
                    maximoEllos = valor;
                    ultimoChutadorRival = indice;
                }
                indice++;
            }
            if (maximoNuestro > maximoEllos) {
                tenemosLaPelota = true;
            } else if (maximoNuestro < maximoEllos) {
                tenemosLaPelota = false;
                
                
            }
        }
    }

    void moverJugadores() {
        //cada jugador ubicarse segun el array posicionMoverA
        for (int i = 0; i < 11; i++) {
            comandos.add(new ComandoIrA(i, posicionMoverA[i]));
        }
    }

    void actualizarVariablesSaque(boolean saco) {
        // Me fijo si estoy en un saque
        // y espero un poco para no apurarme y perderla
        // y los jugadores pueden ir a su posición
        if (!deboSacar && saco) {
            deboSacar = true;
            iterSaco = 20;
        }
        if (iterSaco > 0) {
            iterSaco--;
        }
    }

    void seguirPelota(SituacionPartido sp) {
        //obtiene el indice del jugador mas cercano al balon
    	if (sp.saco() && sp.balon().distancia(Constantes.centroArcoInf) < 20) {
    		comandos.add(new ComandoIrA(Grosso.Rava.ordinal(), sp.balon()));
		}
    	else if (tenemosLaPelota) {
        	
        	for (int indMasCercano : sp.balon().indicesMasCercanos(sp.misJugadores(), indiceOrigenPase.ordinal())) {

                //el jugador mas cercano sigue al balon
                comandos.add(new ComandoIrA(indMasCercano, sp.balon()));
                if (indMasCercano == Grosso.Costilla.ordinal()) {
                    costiquiPicando = false;
                    posicionMoverA[Grosso.Costilla.ordinal()] = alineacion1[Grosso.Costilla.ordinal()];
                }
                
                abejita = getGrosso(indMasCercano);
                
                break;
            }
        }
        else {
        	// Si no tenemos la pelota
        	int ordinalMarcadorAbejita = 0;
        	for (int indMasCercano : sp.balon().indicesMasCercanos(sp.misJugadores())) {
        		ordinalMarcadorAbejita = indMasCercano;
                //el jugador mas cercano sigue al balon
                comandos.add(new ComandoIrA(indMasCercano, sp.balon()));
                
                abejita = getGrosso(indMasCercano);
                
                break;
            }
        	for (int indMasCercano : sp.balon().indicesMasCercanos(sp.misJugadores(), ordinalMarcadorAbejita)) {
        		if (sp.misJugadores()[indMasCercano].getY() < sp.balon().getY()) {
        			//el jugador mas cercano y con menor Y sigue la pelota
                    comandos.add(new ComandoIrA(indMasCercano, getUbicacionTapon(sp.misJugadores()[indMasCercano], sp)));
                    
                    tapon = getGrosso(indMasCercano);
                    
                    break;
				}
            }
		}        
    }

    void arquera(SituacionPartido sp) {
        
    	if ( Constantes.centroArcoInf.distancia(sp.balon()) < 13 && sp.alturaBalon() < Constantes.ALTURA_CONTROL_BALON / 2)
        {
        	comandos.add(new ComandoIrA(Grosso.Rava.ordinal(), sp.balon()));
        }
    	else if (sp.balon().indiceMasCercano(sp.misJugadores(), indiceOrigenPase.ordinal()) == Grosso.Rava.ordinal() &&
                sp.rivales()[ultimoChutadorRival].getY() > (Constantes.centroArcoInf.getY() + Constantes.ANCHO_AREA_GRANDE * 0.8) &&
                !sp.saco()) {
        	
    		// buscar la ecuacion de la recta del arco y la pelota
    		double destinoX = 0;
    		if ( Math.abs(sp.balon().getX() - balonAnterior.getX()) > 0.1) {
	        	double m = (sp.balon().getY() - balonAnterior.getY()) /  (sp.balon().getX() - balonAnterior.getX());
	        	double b = sp.balon().getY() - m * sp.balon().getX();
    		    		
	        	destinoX = (- Constantes.LARGO_CAMPO_JUEGO / 2 - b) / m;
	        	if (destinoX < Constantes.posteIzqArcoInf.getX())
	        		destinoX = Constantes.centroArcoInf.getX();
	        	else if (destinoX > Constantes.posteDerArcoInf.getX())
	        		destinoX = Constantes.centroArcoInf.getX();
    		} else {
    			destinoX = (sp.balon().getX() + balonAnterior.getX()) / 2;
    		}
	        	
	        Posicion p = new Posicion(destinoX, sp.misJugadores()[Grosso.Rava.ordinal()].getY());
	        comandos.add(new ComandoIrA(Grosso.Rava.ordinal(), p));

        } 
        else {
        	comandos.add(new ComandoIrA(Grosso.Rava.ordinal(), posicionMoverA[Grosso.Rava.ordinal()]));
        }
    	
    	for (Posicion rival : sp.rivales()) {
    		if (rival.distancia(sp.balon()) < 5 && rival.getY() > sp.misJugadores()[Grosso.Rava.ordinal()].getY()) {
    			if (rival.getX() > sp.misJugadores()[Grosso.Rava.ordinal()].getX() ) {
    				comandos.add(new ComandoGolpearBalon(Grosso.Rava.ordinal(), 180, 1, true));
    			} else {
    				comandos.add(new ComandoGolpearBalon(Grosso.Rava.ordinal(), 0, 1, true));
    			}
    			return;
    		}
		}
    	
    	// default
    	comandos.add(new ComandoGolpearBalon(Grosso.Rava.ordinal(), Constantes.centroArcoSup, 1, true));
    	
    }



    boolean buscarPase(SituacionPartido sp, int origen, boolean adelante, boolean seguro) {

        //obtiene los jugadores mas cercanos, al jugador que puede rematar
        int[] cercanos = sp.misJugadores()[origen].indicesMasCercanos(sp.misJugadores());
        //encontro un pase en falso
        boolean pase = false;
        boolean alto;

        //recorre los jugadores desde el mas cercano  al mas  lejano

        for (int j = 1; !pase && j < cercanos.length; j++) {

        	boolean joseSeDesmarca = false, costiquiSeDesmarca = false;
        	Posicion desmarque = new Posicion(0,0);
        	
        	// chequeo posible desmarque para josexo
        	if (cercanos[j] == Grosso.Josexo.ordinal()) {
        		for (Posicion rival : sp.rivales()) {
            		if (rival.distancia(sp.misJugadores()[cercanos[j]]) < 5 ) {
            			
            			boolean funcionaIzq = true;
            			// pruebo a la izq
            			Posicion desmarqueAux = sp.misJugadores()[cercanos[j]].moverPosicion(- 10, 0);
            			for (Posicion rival2 : sp.rivales()) {
                    		if (rival2.distancia(desmarqueAux) < 5 ) {
                    			funcionaIzq = false;
                    			break;                    			
                    		}
            			}
            			
            			boolean funcionaDer = false;
            			if (!funcionaIzq) {
            				funcionaDer = true;
	            			// pruebo a la izq
	            			desmarqueAux = sp.misJugadores()[cercanos[j]].moverPosicion(10, 0);
	            			for (Posicion rival3 : sp.rivales()) {
	                    		if (rival3.distancia(desmarqueAux) < 5 ) {
	                    			funcionaDer = true;
	                    			break;
	                    		}
	                    	}
            			}
            			
            			if (funcionaDer || funcionaIzq) {
            				joseSeDesmarca = true;
            				desmarque = desmarqueAux;
            			}
            			break;
            		}
        		}
        	} else if (cercanos[j] == Grosso.Costilla.ordinal()) {
        		for (Posicion rival : sp.rivales()) {
            		if (rival.distancia(sp.misJugadores()[cercanos[j]]) < 5 ) {
            			
            			boolean funcionaIzq = true;
            			// pruebo a la izq
            			Posicion desmarqueAux = sp.misJugadores()[cercanos[j]].moverPosicion(- 10, 0);
            			for (Posicion rival2 : sp.rivales()) {
                    		if (rival2.distancia(desmarqueAux) < 5 ) {
                    			funcionaIzq = false;
                    			break;                    			
                    		}
            			}
            			
            			if (funcionaIzq) {
            				costiquiSeDesmarca = true;
            				desmarque = desmarqueAux;
            			}
            			break;
            		}
        		}
        	}
        	
        	Posicion destinoPase = sp.misJugadores()[cercanos[j]];
        	if (joseSeDesmarca || costiquiSeDesmarca) {
        		destinoPase = desmarque;
        	}
        	
            boolean valido = true;
            if (adelante) {
                valido = sp.misJugadores()[origen].getY() < destinoPase.getY();
            } else {
                valido = sp.misJugadores()[origen].getY() >= destinoPase.getY();
            }
            if (seguro) {
                valido = valido && paseSeguro(sp.misJugadores()[origen], destinoPase, sp);
            }

            if (valido) {
                //dar pase al jugador mas cercano que este en posicion mas ofensiva
                double distancia = sp.misJugadores()[origen].distancia(destinoPase);
                if ( distancia > 20) {
                    alto = true;
                } else {
                    alto = false;
                }

                // Fuerza, dependiendo de la distancia y fuerza del jugador
                double fuerza = 0.5;
                double fuerzaJugador = sp.detalleMisJugadores()[origen].getRemate();
                if (distancia > 30) distancia = 15;

                fuerza = (0.5 / fuerzaJugador) * ((distancia + 15) / 30);

                comandos.add(new ComandoGolpearBalon(origen, destinoPase, fuerza /*.5 + rand.nextDouble() * .5*/, alto));
                //encontro un pase por lo menos
                pase = true;

                indiceDestinoPase = getGrosso(cercanos[j]);
                indiceOrigenPase = getGrosso(origen);
            }
        }

        return pase;
    }

    void ataque(SituacionPartido sp) {
        //obtiene un arreglo con los indices de los jugadores que pueden rematar
        int rematan[] = sp.puedenRematar();

        //recorre los jugadores que pueden rematar
        for (int i : rematan) {
            //Tiro al arco
            //si el jugador que remata esta a menos de 30 metros del arco rival -> remata al arco
        	
        	int indicePorteroRival = 0;
            for (int j = 0; j < 11; j++) {
                if (sp.detalleJugadoresRivales()[j].esPortero()) {
                    indicePorteroRival = j;
                    break;
                }
            }
            
            
        	// Elijo palo 
        	double anguloArquero = sp.misJugadores()[i].angulo(sp.rivales()[indicePorteroRival]);
        	double anguloPaloDerecha = sp.misJugadores()[i].angulo(Constantes.posteDerArcoSup);
        	double anguloPaloIzquierda = sp.misJugadores()[i].angulo(Constantes.posteIzqArcoSup);
        	
        	double anguloDerecha = anguloArquero - anguloPaloDerecha;
        	double anguloIzquierda = anguloPaloIzquierda - anguloArquero;
        	
        	double anguloTiro = 0;
        	if (anguloArquero < anguloPaloDerecha || anguloArquero > anguloPaloIzquierda) {
        	
        		anguloTiro = sp.misJugadores()[i].angulo(Constantes.centroArcoSup);
        	
        	} else {
        	
	        	if (anguloDerecha > anguloIzquierda) {
	        		anguloTiro = anguloDerecha / 2 + anguloPaloDerecha;
	        	} else {
	        		anguloTiro = anguloIzquierda / 2 + anguloArquero;
	        	}
        	
        	}
        	
        	anguloTiro = anguloTiro * 180 / Math.PI;
        	
            if (sp.misJugadores()[i].distancia(Constantes.centroArcoSup) < Constantes.ANCHO_AREA_GRANDE) {
                // Josexy
      	
                if ((i != Grosso.Josexo.ordinal())) {
                    comandos.add(new ComandoGolpearBalon(i, anguloTiro, 1, false)); //i, Constantes.centroArcoSup.moverPosicion(tmp, 0), 1, false));
                } else {
                    // La tiene Jose(x,y)
                    
                    if (sp.rivales()[indicePorteroRival].distancia(Constantes.centroArcoSup) < 3.5) {
                        // Patea mi amigo Josex
                        // y
                        comandos.add(new ComandoGolpearBalon(i, anguloTiro, 1, false)); //(i, Constantes.centroArcoSup.moverPosicion(tmp, 0), 1, false));
                    } else {
                        // Sino pase a costiqui
                        if (costiquiPicando) {
                            comandos.add(new ComandoGolpearBalon(i, posicionMoverA[Grosso.Costilla.ordinal()], 0.7, false));
                        } else {
                            comandos.add(new ComandoGolpearBalon(i, sp.misJugadores()[Grosso.Costilla.ordinal()], 0.7, false));
                        }
                    }
                }

            } else if (sp.misJugadores()[i].distancia(Constantes.centroArcoSup) < Constantes.ANCHO_AREA_GRANDE + 5) {
                comandos.add(new ComandoGolpearBalon(i, anguloTiro, 1, false)); //(new ComandoGolpearBalon(i, Constantes.centroArcoSup.moverPosicion(tmp, 0), 1, 15));
            } else if (sp.misJugadores()[i].distancia(Constantes.centroArcoSup) <= 25) {
            	double anguloAlto = sp.misJugadores()[i].distancia(Constantes.centroArcoSup);
                comandos.add(new ComandoGolpearBalon(i, anguloTiro, 1, anguloAlto));
            } else if (sp.misJugadores()[i].distancia(Constantes.centroArcoSup) < 30) {
                comandos.add(new ComandoGolpearBalon(i, Constantes.centroArcoSup, 1, true));
            }
        }

        // El pase va a josexo, atento costilla
        // No lo hago si costilla esta siguiendo la bola
        int indMasCercano = sp.balon().indiceMasCercano(sp.misJugadores(), indiceOrigenPase.ordinal());
        if (indMasCercano != Grosso.Costilla.ordinal() &&
                (indiceDestinoPase == Grosso.Josexo || indiceDestinoPase == Grosso.Costilla)) {
            if (!costiquiPicando) {
                // diferencia entre josexy y costiqui
                double adelantar = sp.misJugadores()[Grosso.Josexo.ordinal()].getY() - sp.misJugadores()[Grosso.Costilla.ordinal()].getY() + 4.;
                // avanti costilla
                posicionMoverA[Grosso.Costilla.ordinal()] =
                        alineacion1[Grosso.Costilla.ordinal()].moverPosicion(0, adelantar);
                comandos.add(new ComandoIrA(Grosso.Costilla.ordinal(), posicionMoverA[Grosso.Costilla.ordinal()]));
                costiquiPicando = true;
            }
        } else {
            costiquiPicando = false;
            posicionMoverA[Grosso.Costilla.ordinal()] = alineacion1[Grosso.Costilla.ordinal()];
        }
    }

    boolean posicionEnRectangulo(Posicion p, Posicion vertice1, Posicion vertice2) {
        double xMin = Math.min(vertice1.getX(), vertice2.getX());
        double xMax = Math.max(vertice1.getX(), vertice2.getX());
        double yMin = Math.min(vertice1.getY(), vertice2.getY());
        double yMax = Math.max(vertice1.getY(), vertice2.getY());

        if ( xMin <= p.getX() && p.getX() <= xMax &&
                yMin <= p.getY() && p.getY() <= yMax) {
                    return true;
        }
        return false;
    }

    Posicion getPosicionMarcaJugador(Posicion atacante, SituacionPartido sp) {
        Posicion res = new Posicion();

        // me paro a un metro del rival
        double offset = 2.5;

        double r = Math.abs(sp.balon().getY() - atacante.getY() / (sp.balon().getX() - atacante.getX()));
        double x = Math.sqrt( ( offset * offset ) / (r * r + 1) );
        double y = r * x;

        if (atacante.getY() > sp.balon().getY()) {
            y = -y;
        }
            
        if (atacante.getX() > sp.balon().getX()) {
            x = -x;
        }

        res = atacante.moverPosicion(x, y);

        return res;
    }

    boolean posicionEnCuadranteDefensa(Posicion p, CuadranteDefensa cuadrante) {
        // Cambio de coordenadas, 0,0 en Constantes.cornerInfIzq
        p = p.moverPosicion(Constantes.ANCHO_CAMPO_JUEGO / 2, Constantes.LARGO_CAMPO_JUEGO / 2);
        
        if (cuadrante == CuadranteDefensa.ExtremoIzquierda) {
            return posicionEnRectangulo(p, new Posicion(0,0), 
                    new Posicion(Constantes.ANCHO_CAMPO_JUEGO / 4, Constantes.LARGO_CAMPO_JUEGO / 3));
            
        } else if (cuadrante == CuadranteDefensa.MedioIzquierda) {
            return posicionEnRectangulo(p, new Posicion(Constantes.ANCHO_CAMPO_JUEGO / 4, 0), 
                    new Posicion(Constantes.ANCHO_CAMPO_JUEGO / 2, Constantes.LARGO_CAMPO_JUEGO / 3));
            
        } else if (cuadrante == CuadranteDefensa.MedioDerecha) {
            return posicionEnRectangulo(p, new Posicion(Constantes.ANCHO_CAMPO_JUEGO / 2, 0), 
                    new Posicion( 3 * Constantes.ANCHO_CAMPO_JUEGO / 4, Constantes.LARGO_CAMPO_JUEGO / 3));
            
        } else if (cuadrante == CuadranteDefensa.ExtremoDerecha) {
            return posicionEnRectangulo(p, new Posicion(3 * Constantes.ANCHO_CAMPO_JUEGO / 4, 0), 
                    new Posicion(Constantes.ANCHO_CAMPO_JUEGO, Constantes.LARGO_CAMPO_JUEGO / 3));
            
        }
        return false;
    }

    void defensa(SituacionPartido sp) {

        Grosso[] tomadoPor = new Grosso[11];
        for (int i=0; i<11; i++) {
            tomadoPor[i] = Grosso.Ninguno;
        }

        posicionMoverA[Grosso.Manolo.ordinal()] = alineacion1[Grosso.Manolo.ordinal()];
        posicionMoverA[Grosso.Diegoa.ordinal()] = alineacion1[Grosso.Diegoa.ordinal()];
        posicionMoverA[Grosso.Cyber.ordinal()] = alineacion1[Grosso.Cyber.ordinal()];
        posicionMoverA[Grosso.Pasto.ordinal()] = alineacion1[Grosso.Pasto.ordinal()];
        posicionMoverA[Grosso.Pela.ordinal()] = alineacion1[Grosso.Pela.ordinal()];
        posicionMoverA[Grosso.Bochae.ordinal()] = alineacion1[Grosso.Bochae.ordinal()];

        if (!tenemosLaPelota || getSector(sp) == Sector.Defensa) {
            // Setear marcas
            int indiceRival = 0;
            int indiceMasCercaArco = -1;
            double minDistanciaArco = Constantes.LARGO_CAMPO_JUEGO;
            // Jorgeao
            if (tapon != Grosso.Manolo && abejita != Grosso.Manolo) {
	            for (Posicion rival : sp.rivales()) {
	                if (posicionEnCuadranteDefensa(rival, CuadranteDefensa.MedioDerecha)) {
	                    if (minDistanciaArco > rival.distancia(Constantes.centroArcoInf)) {
	                        minDistanciaArco = rival.distancia(Constantes.centroArcoInf);
	                        indiceMasCercaArco = indiceRival;
	                    }
	                }
	                indiceRival++;
	            }
	            if (indiceMasCercaArco > -1) {
	                tomadoPor[indiceMasCercaArco] = Grosso.Manolo;
	                posicionMoverA[Grosso.Manolo.ordinal()] = getPosicionMarcaJugador(sp.rivales()[indiceMasCercaArco], sp);
	            }
            }
            
            // Diega
            if (tapon != Grosso.Diegoa && abejita != Grosso.Diegoa) {
	            indiceRival = 0;
	            indiceMasCercaArco = -1;
	            minDistanciaArco = Constantes.LARGO_CAMPO_JUEGO;
	            for (Posicion rival : sp.rivales()) {
	                if (tomadoPor[indiceRival] == Grosso.Ninguno &&
	                        posicionEnCuadranteDefensa(rival, CuadranteDefensa.MedioIzquierda)) {
	                    if (minDistanciaArco > rival.distancia(Constantes.centroArcoInf)) {
	                        minDistanciaArco = rival.distancia(Constantes.centroArcoInf);
	                        indiceMasCercaArco = indiceRival;
	                    }
	                }
	                indiceRival++;
	            }
	            if (indiceMasCercaArco > -1) {
	                tomadoPor[indiceMasCercaArco] = Grosso.Diegoa;
	                posicionMoverA[Grosso.Diegoa.ordinal()] = getPosicionMarcaJugador(sp.rivales()[indiceMasCercaArco], sp);
	            }
            }

            if (tapon != Grosso.Cyber && abejita != Grosso.Cyber) {
	            // Rodrigo - buscando ayudar a Jorgeao
	            indiceRival = 0;
	            indiceMasCercaArco = -1;
	            minDistanciaArco = Constantes.LARGO_CAMPO_JUEGO;
	            for (Posicion rival : sp.rivales()) {
	                if (tomadoPor[indiceRival] == Grosso.Ninguno &&
	                        posicionEnCuadranteDefensa(rival, CuadranteDefensa.MedioDerecha)) {
	                    if (minDistanciaArco > rival.distancia(Constantes.centroArcoInf)) {
	                        minDistanciaArco = rival.distancia(Constantes.centroArcoInf);
	                        indiceMasCercaArco = indiceRival;
	                    }
	                }
	                indiceRival++;
	            }
	            if (indiceMasCercaArco > -1) {
	                tomadoPor[indiceMasCercaArco] = Grosso.Cyber;
	                posicionMoverA[Grosso.Cyber.ordinal()] = getPosicionMarcaJugador(sp.rivales()[indiceMasCercaArco], sp);
	            }
	
	            // Rodrigo en su cuadrante
	            indiceRival = 0;
	            indiceMasCercaArco = -1;
	            minDistanciaArco = Constantes.LARGO_CAMPO_JUEGO;
	            for (Posicion rival : sp.rivales()) {
	                if (tomadoPor[indiceRival] == Grosso.Ninguno &&
	                        posicionEnCuadranteDefensa(rival, CuadranteDefensa.ExtremoDerecha)) {
	                    if (minDistanciaArco > rival.distancia(Constantes.centroArcoInf)) {
	                        minDistanciaArco = rival.distancia(Constantes.centroArcoInf);
	                        indiceMasCercaArco = indiceRival;
	                    }
	                }
	                indiceRival++;
	            }
	            if (indiceMasCercaArco > -1) {
	                tomadoPor[indiceMasCercaArco] = Grosso.Cyber;
	                posicionMoverA[Grosso.Cyber.ordinal()] = getPosicionMarcaJugador(sp.rivales()[indiceMasCercaArco], sp);
	            }
            }


            if (tapon != Grosso.Pasto && abejita != Grosso.Pasto) {
	            // Pasto - buscando ayudar a Diega
	            indiceRival = 0;
	            indiceMasCercaArco = -1;
	            minDistanciaArco = Constantes.LARGO_CAMPO_JUEGO;
	            for (Posicion rival : sp.rivales()) {
	                if (tomadoPor[indiceRival] == Grosso.Ninguno &&
	                        posicionEnCuadranteDefensa(rival, CuadranteDefensa.MedioIzquierda)) {
	                    if (minDistanciaArco > rival.distancia(Constantes.centroArcoInf)) {
	                        minDistanciaArco = rival.distancia(Constantes.centroArcoInf);
	                        indiceMasCercaArco = indiceRival;
	                    }
	                }
	                indiceRival++;
	            }
	            if (indiceMasCercaArco > -1) {
	                tomadoPor[indiceMasCercaArco] = Grosso.Pasto;
	                posicionMoverA[Grosso.Pasto.ordinal()] = getPosicionMarcaJugador(sp.rivales()[indiceMasCercaArco], sp);
	            }
	            // Pasto en su cuadrante
	            indiceRival = 0;
	            indiceMasCercaArco = -1;
	            minDistanciaArco = Constantes.LARGO_CAMPO_JUEGO;
	            for (Posicion rival : sp.rivales()) {
	                if (tomadoPor[indiceRival] == Grosso.Ninguno &&
	                        posicionEnCuadranteDefensa(rival, CuadranteDefensa.ExtremoIzquierda)) {
	                    if (minDistanciaArco > rival.distancia(Constantes.centroArcoInf)) {
	                        minDistanciaArco = rival.distancia(Constantes.centroArcoInf);
	                        indiceMasCercaArco = indiceRival;
	                    }
	                }
	                indiceRival++;
	            }
	            if (indiceMasCercaArco > -1) {
	                tomadoPor[indiceMasCercaArco] = Grosso.Pasto;
	                posicionMoverA[Grosso.Pasto.ordinal()] = getPosicionMarcaJugador(sp.rivales()[indiceMasCercaArco], sp);
	            }
            }

            if (tapon != Grosso.Pela && abejita != Grosso.Pela) {
	            // Pela baja a ayudar?? o se le caen las bolas
	            indiceRival = 0;
	            indiceMasCercaArco = -1;
	            minDistanciaArco = Constantes.LARGO_CAMPO_JUEGO;
	            for (Posicion rival : sp.rivales()) {
	                if (tomadoPor[indiceRival] == Grosso.Ninguno &&
	                        posicionEnCuadranteDefensa(rival, CuadranteDefensa.ExtremoDerecha)) {
	                    if (minDistanciaArco > rival.distancia(Constantes.centroArcoInf)) {
	                        minDistanciaArco = rival.distancia(Constantes.centroArcoInf);
	                        indiceMasCercaArco = indiceRival;
	                    }
	                }
	                indiceRival++;
	            }
	            if (indiceMasCercaArco > -1) {
	                tomadoPor[indiceMasCercaArco] = Grosso.Pela;
	                posicionMoverA[Grosso.Pela.ordinal()] = getPosicionMarcaJugador(sp.rivales()[indiceMasCercaArco], sp);
	            }
            }

            if (tapon != Grosso.Bochae && abejita != Grosso.Bochae) {
	            // Bocha baja a ayudar?? que grosso
	            indiceRival = 0;
	            indiceMasCercaArco = -1;
	            minDistanciaArco = Constantes.LARGO_CAMPO_JUEGO;
	            for (Posicion rival : sp.rivales()) {
	                if (tomadoPor[indiceRival] == Grosso.Ninguno &&
	                        posicionEnCuadranteDefensa(rival, CuadranteDefensa.ExtremoIzquierda)) {
	                    if (minDistanciaArco > rival.distancia(Constantes.centroArcoInf)) {
	                        minDistanciaArco = rival.distancia(Constantes.centroArcoInf);
	                        indiceMasCercaArco = indiceRival;
	                    }
	                }
	                indiceRival++;
	            }
	            if (indiceMasCercaArco > -1) {
	                tomadoPor[indiceMasCercaArco] = Grosso.Bochae;
	                posicionMoverA[Grosso.Bochae.ordinal()] = getPosicionMarcaJugador(sp.rivales()[indiceMasCercaArco], sp);
	            }
            }

        }

        // Delanteros apretando en la salida
        if (sp.sacaRival() && sp.balon().getY() > 0) {

            // Josexo
            int indiceRival = 0;
            int indiceMasCercaArco = -1;
            double minDistanciaArco = Constantes.LARGO_CAMPO_JUEGO;
            for (Posicion rival : sp.rivales()) {
                if (! sp.detalleJugadoresRivales()[indiceRival].esPortero()) {
                    if (tomadoPor[indiceRival] == Grosso.Ninguno) {
                        if (minDistanciaArco > rival.distancia(Constantes.centroArcoSup)) {
                            minDistanciaArco = rival.distancia(Constantes.centroArcoSup);
                            indiceMasCercaArco = indiceRival;
                        }
                    }
                }
                indiceRival++;
            }
            if (indiceMasCercaArco > -1) {
                tomadoPor[indiceMasCercaArco] = Grosso.Josexo;
                posicionMoverA[Grosso.Josexo.ordinal()] = getPosicionMarcaJugador(sp.rivales()[indiceMasCercaArco], sp);
            }


            // Costilla
            // TODO: que pasa cuando costilla pica

            indiceRival = 0;
            indiceMasCercaArco = -1;
            minDistanciaArco = Constantes.LARGO_CAMPO_JUEGO;
            for (Posicion rival : sp.rivales()) {
                if (! sp.detalleJugadoresRivales()[indiceRival].esPortero()) {
                    if (tomadoPor[indiceRival] == Grosso.Ninguno) {
                        if (minDistanciaArco > rival.distancia(Constantes.centroArcoSup)) {
                            minDistanciaArco = rival.distancia(Constantes.centroArcoSup);
                            indiceMasCercaArco = indiceRival;
                        }
                    }
                }
                indiceRival++;
            }
            if (indiceMasCercaArco > -1) {
                tomadoPor[indiceMasCercaArco] = Grosso.Costilla;
                posicionMoverA[Grosso.Costilla.ordinal()] = getPosicionMarcaJugador(sp.rivales()[indiceMasCercaArco], sp);
            }
        }
    }

    boolean esSaqueDesdeDefensa(SituacionPartido sp) {
        return sp.saco() && sp.balon().getY() < 0;
    }
    
    Posicion getUbicacionTapon(Posicion marcador, SituacionPartido sp) {
    	Posicion resultado = new Posicion(0,0);
    	
    	Posicion balon = sp.balon();
    	Posicion arco = Constantes.centroArcoInf;
    	
    	if (Math.abs(balon.getX() - arco.getX()) < 0.5) {
    		return new Posicion(balon.getX(), marcador.getY());
    	}
    	
    	// buscar la ecuacion de la recta del arco y la pelota
    	double m = (balon.getY() - arco.getY()) /  (balon.getX() - arco.getX());
    	double b = balon.getY() - m * balon.getX();
    	
    	// distancia mas corta del jugador a la recta 
    	double distRecta = Math.abs(m * marcador.getX() - marcador.getY() + b) / Math.sqrt(m*m + 1);
    	
    	double distJugadorPelotaCuadrado = (balon.getY() - marcador.getY())*(balon.getY() - marcador.getY()) +
    			(balon.getX() - marcador.getX())*(balon.getX() - marcador.getX());
    	
    	double distPelotaPuntoTaponCuadrado = distJugadorPelotaCuadrado - distRecta*distRecta;
    	
    	double R = Math.abs((balon.getY() - arco.getY())/(balon.getX() - arco.getX()));
    	
    	double deltaX = Math.sqrt( distPelotaPuntoTaponCuadrado / (R*R + 1) );
    	double deltaY = R * deltaX;
    	
    	if (balon.getX() > arco.getX()) {
    		resultado = resultado.setPosicion(balon.getX() - deltaX, balon.getY() - deltaY);
    	} else {
    		resultado = resultado.setPosicion(balon.getX() + deltaX, balon.getY() - deltaY);
    	}
    	
    	
    	return resultado;
    }

    public List<Comando> ejecutar(SituacionPartido sp) {
        //vacia la lista de comandos
        comandos.clear();
        
        arquera(sp);

        // Actualizamos las variables si estmos en saque
        actualizarVariablesSaque(sp.saco());

        // Actualizamos la flag si tenemos la bochae
        actualizarTenemosLaPelota(sp);

        //Mover los jugadores segun el array
        moverJugadores();

        if (iterSaco == 0) {
            deboSacar = sp.saco();

            // Hay que seguir la bola
            seguirPelota(sp);

            //obtiene un arreglo con los indices de los jugadores que pueden rematar
            int rematan[] = sp.puedenRematar();

            if (!tenemosLaPelota) {
                // El rival le pegó a la pelota o está en un saque
                indiceDestinoPase = Grosso.Ninguno;
                indiceOrigenPase = Grosso.Ninguno;
            }

            //recorre los jugadores que pueden rematar
            for (int i : rematan) {

                // Gente que le pegue para arriba
                if (i == Grosso.Rava.ordinal() /* rava y los defensas horribles pegenlenenenenenenenennn */) {
                    comandos.add(new ComandoGolpearBalon(i, Constantes.centroArcoSup, 1, true));
                    indiceDestinoPase = Grosso.Ninguno;
                    indiceOrigenPase = Grosso.Ninguno;
                    return comandos;
                }
                if (i == Grosso.Pasto.ordinal() || i == Grosso.Cyber.ordinal() /* rava y los defensas horribles pegenlenenenenenenenennn */) {
                    comandos.add(new ComandoGolpearBalon(i, Constantes.centroCampoJuego, 1, true));
                    indiceDestinoPase = Grosso.Ninguno;
                    indiceOrigenPase = Grosso.Ninguno;
                    return comandos;
                }

                boolean pase = false;

                boolean saco_defensa = esSaqueDesdeDefensa(sp);

                if (!saco_defensa) {

                    // Busco pase seguro hacia adelante
                    pase = buscarPase(sp, i, true, true);

                    //si no busco pase inseguro hacia adelante
                    if (!pase) {
                        pase = buscarPase(sp, i, true, false);
                    }

                    //si no busco pase seguro hacia atras
                    if (!pase) {
                        pase = buscarPase(sp, i, false, true);
                    }
                }

                // mmm que corno hacemos con la pelota, o pase inseguro atras o la reventamos
                if (!pase) {
                    if (getSector(sp) == Sector.Ataque && !saco_defensa) {
                        pase = buscarPase(sp, i, false, false);
                    } else {
                        comandos.add(new ComandoGolpearBalon(i, Constantes.centroArcoSup, 1, true));
                        indiceDestinoPase = Grosso.Ninguno;
                        indiceOrigenPase = Grosso.Ninguno;
                        return comandos;
                    }
                }
            }

            // Ataque, todos al ataque
            ataque(sp);
            
            // Seguir con la pelota
            for (int i : rematan) {

                // Me fijo si el bocha le puede pegar al esferico
                if (i == Grosso.Bochae.ordinal() || i == Grosso.Montico.ordinal() || i == Grosso.Pela.ordinal() || i == Grosso.Costilla.ordinal()) {
                	for (int indMasCercano : sp.balon().indicesMasCercanos(sp.rivales())) {
                		
                		if (sp.misJugadores()[i].distancia(sp.rivales()[indMasCercano]) > 5 &&
                				sp.misJugadores()[i].distancia(Constantes.centroArcoSup) > 25) {
                        	comandos.add(new ComandoIrA(i, Constantes.centroArcoSup));
    						comandos.add(new ComandoGolpearBalon(i));
    						indiceDestinoPase = Grosso.Ninguno;
    						indiceOrigenPase = Grosso.Ninguno;
    					}
                		break;
                	}                	
                }
                /*if (i == Grosso.Josexo.ordinal()) {                	
                    if (sp.misJugadores()[i].distancia(Constantes.centroArcoSup) < 30 && sp.misJugadores()[i].distancia(Constantes.centroArcoSup) > 5 && cantidadGolpearBalon == 0) {
                    	comandos.add(new ComandoIrA(i, Constantes.centroArcoSup));
						comandos.add(new ComandoGolpearBalon(i));
						indiceDestinoPase = Grosso.Ninguno;
						indiceOrigenPase = Grosso.Ninguno;
						cantidadGolpearBalon++;
					}
                    else {
						cantidadGolpearBalon = 0;
					}
                }
                else {
                	cantidadGolpearBalon = 0;
				}*/
            }

            // Hay que defender muchachooooo
            defensa(sp);

            // Movimiento del arquero
            arquera(sp);
            
            
        }
        
        balonAnterior = sp.balon();
        
        //retorna la lista de comandos.
        return comandos;
    }
}